package hw2.cs236369.solution;

import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Scanner;
import java.util.Set;

import hw2.cs236369.HTMLParser;

// for the reading of the webpages 
import java.net.*;
import java.io.*;

import Logging.Monitor;

/**
 * Our implementation MLof the HTMLParser interface.
 * Uses an inner class named MyHtmlTag that encapsulates a tag.
 * @author ron&artem
 */
public class MyParser implements HTMLParser{
	
	private LinkedHashSet<MyHtmlTag> tagSet = new LinkedHashSet<MyHtmlTag>();

	/**
	 * Encapsulates our html tags. Also holds the number of their occurrences
	 * @author ron&artem
	 */
	class MyHtmlTag {
		
		// saves the entire tag and his attributes
		private String tag;
		private int count = 1;
		
		public MyHtmlTag(String tag) {
			this.tag = tag;
		}
		
		public void increment() {
			count++;
		}
		
		public int getCount() {
			return count;
		}
		
		public String getTag(){
			return tag;
		}
		
		@Override
		public boolean equals(Object obj) {
			 
			return tag.hashCode()==obj.hashCode();
			
		}
		
		@Override
		public int hashCode() {
			return tag.hashCode();
		}
		
	}
	
	@Override
	public void parseTags(String url) throws MalformedURLException {
		
		// If the string specifies an unknown protocol throws MalformedURLException
		URL siteAddress = new URL(url); 
		
		URLConnection yc = null;
		BufferedReader in = null;
		
		try {
			
			yc = siteAddress.openConnection();
			in = new BufferedReader(new InputStreamReader(yc.getInputStream()));
			Monitor.write(this, "Created the buffer reader for our http");
			
			// setting the regular expression for finding the html tags
			Scanner tagPraser = new Scanner(in);
			tagPraser.useDelimiter(">[^<]*<"); // this delimiter is actually >...< 
			Monitor.write(	this,"start token stream with delimiter :" + 
							tagPraser.delimiter().toString());

			// saving the tokens
			boolean flag = false;
			String curTagString = "";
			while(tagPraser.hasNext())
			{
				// adding the closing tag to each tag except the first 
				if (flag) {
					curTagString = "<";
				} else {
					flag = true; 
				}
				curTagString += tagPraser.next();
				
				// closing each tag except the last one
				if (tagPraser.hasNext()) {
					curTagString += ">";
				}
				
				// saving the tag in our data-structure
				MyHtmlTag curTag = new MyHtmlTag(curTagString);
				Monitor.write(this, "Adding the tag:"+curTagString);
				if(!tagSet.add(curTag)) {
					// if already contains add will return false
					for (Iterator<MyHtmlTag> iterator = tagSet.iterator(); 
														iterator.hasNext();) {
						
						MyHtmlTag searchedTag = iterator.next();
						if(searchedTag.equals(curTag)){
							
							Monitor.write(this, "Incrementing the count of tag:"+curTagString);
							searchedTag.increment();
							break;
						}
					}
				}
				
			} // parsing the html
			Monitor.write(this,"end token stream");
			
			in.close();
			
		} catch (IOException e) {
			Monitor.writeException(e);
		} finally {
			
			try {
				in.close();
			} catch (IOException e) {
				Monitor.writeException(e);
			}
			
		} // finally
		
	} // parseTags

	@Override
	public Set<String> getTags() {
		
		Set<String> $ = new LinkedHashSet<String>();
		for (Iterator<MyHtmlTag> iterator = tagSet.iterator(); iterator.hasNext();) {
			
			MyHtmlTag searchedTag = iterator.next();
			Monitor.write(this, "Getting these elements:"
								+searchedTag.getTag()+" "+searchedTag.getCount());
			$.add(searchedTag.getTag());
		}
		return $;
	}

	@Override
	public HashMap<String, Integer> getTagsCount() {
		
		HashMap<String,Integer> $ = new HashMap<String, Integer>(); 
		Monitor.write(this, "Adding the elements from our tagSet to the HashMap");
		for (Iterator<MyHtmlTag> iterator = tagSet.iterator(); iterator.hasNext();) {
			
			MyHtmlTag searchedTag = iterator.next();
			Monitor.write(this, "Adding these elements:"
								+searchedTag.getTag()+" "+searchedTag.getCount());
			$.put(searchedTag.getTag(), searchedTag.getCount());
		}
		
		return $;
	}
	
}
